package aparicio.mbox.parse;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**A type of email file using the mbox format
 * 
 * @author Trevor
 */
public class MboxFile implements Email{

	private String _file;
		
	private List<String> sentIP;
	private List<String> receivedIP;
	private List<String> sentEmail;
	private List<String> receivedEmail;
	private List<String> sentDomain;
	private List<String> receivedDomain;
	private List<String> sentSubject;
	private List<String> receivedSubject;
	
	private Pattern sentIpPattern = Pattern.compile("^Received: from .* \\[\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\]");
	private Pattern receivedIpPattern =  Pattern.compile("^Received: by \\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
	private Pattern ipPattern = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
	private Pattern sentEmailPattern = Pattern.compile("^From [_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,}) ");
	private Pattern receivedEmailPattern = Pattern.compile("^Delivered-To: [_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})");
	private Pattern emailPattern = Pattern.compile("[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})");
	private Pattern sentDomainPattern = Pattern.compile("^Return-Path: <[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@([A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,}))");
	private boolean sentDomainParsed = false;
	private Pattern receivedDomainPattern = Pattern.compile("^Delivered-To: [_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@([A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,}))");
	private Pattern sentSubjectPattern = Pattern.compile("Subject: (.*)");
	
	private String lastParsedSentIp;
	private String lastParsedReceivedIp;
	
	
	/**Basic constructor for the MboxFile class
	 * 
	 * @param file The .mbox file
	 */
	public MboxFile(String file) {
		_file = file;
		
		sentIP = new CountItemsList<String>();
		receivedIP = new CountItemsList<String>();
		sentEmail = new CountItemsList<String>();
		receivedEmail = new CountItemsList<String>();
		sentDomain = new CountItemsList<String>();
		receivedDomain = new CountItemsList<String>();
		sentSubject = new CountItemsList<String>();
		receivedSubject = new CountItemsList<String>();
	}
	
	@Override
	public void parse() throws Exception {
		// make sure the file is an mbox file
		if (!_file.contains(".mbox"))
			throw new Exception("The file, " + _file + ", is not a .mbox file");
		
		// set up the mbox file for reading
		BufferedReader br;
		try {
			
			br = new BufferedReader(new FileReader(_file));
			
		} catch (FileNotFoundException e) {
			
			System.out.println("MboxFile: File not found: " + _file + " with error: " + e.getMessage());
			throw new Exception("MboxFile: File not found: " + _file);
			
		}
		
		String line;
		try {
			
			// read the mbox file line-by-line
			while ((line = br.readLine()) != null) {
				
				// sent ip address
				parseSentIP(line);
				
				// received ip address
				parseReceivedIP(line);
					
				// sent email address
				parseSentEmail(line);
				
				// received email address
				parseReceivedEmail(line);
				
				// sent domain
				parseSentDomain(line);
				
				// received domain
				parseReceivedDomain(line);
				
				// sent subject line
				parseSubject(line);
			}
			
		} catch (IOException e) {
			
			System.out.println("MboxFile: Failed to read file: " + _file + " with error: " + e.getMessage());
			throw new Exception("MboxFile: Failed to read file: " + _file);
			
		} finally {
			
			// attempt to close the BufferedReader
			try {
				br.close();
			} catch (IOException e) {
				System.out.println("MboxFile: Failed to close the BufferedReader with error: " + e.getMessage());
				throw new Exception("MboxFile: Failed to close the BufferedReader");
			}
		}
	}
	
	/**Parse out the sent IP address based on the defined regex for the mbox format.
	 * 
	 * @param text The text to parse and see if the sent IP is in it
	 * 
	 * @throws Exception
	 */
	private void parseSentIP(String text) throws Exception {
		try {
			// sent ip pattern match
			Matcher sentIpMatch = sentIpPattern.matcher(text);
			
			while (sentIpMatch.find()) {
				// pull out the ip address from the line
				Matcher matcher = ipPattern.matcher(sentIpMatch.group());
		        matcher.find();
		        String ip = matcher.group();
		        sentIP.add("<" + ip + ">");
		        
		        lastParsedSentIp = ip;
			}
		} catch (Exception e) {
			throw new Exception("Failed to match the sender IP address regex: " + e.getMessage());
		}
	}
	
	/**Parse out the received IP address based on the defined regex for the mbox format.
	 * 
	 * @param text The text to parse and see if the recieved IP is in it
	 * 
	 * @throws Exception
	 */
	private void parseReceivedIP(String text) throws Exception {
		try {
			// received ip pattern match
			Matcher receivedIpMatch = receivedIpPattern.matcher(text);
			
			while (receivedIpMatch.find()) {
				// pull out the ip address from the line
				Matcher matcher = ipPattern.matcher(receivedIpMatch.group());
		        matcher.find();
		        String ip = matcher.group();
		        receivedIP.add("<" + ip + ">");
		        
		        lastParsedReceivedIp = ip;
			}
		} catch (Exception e) {
			throw new Exception("Failed to match the receiver IP address regex: " + e.getMessage());
		}
	}
	
	/**Parse out the sent email address based on the defined regex for the mbox format.
	 * 
	 * @param text The text to parse and see if the sent email address is in it
	 * 
	 * @throws Exception
	 */
	private void parseSentEmail(String text) throws Exception {
		try {
			// sent email pattern match
			Matcher sentEmailMatch = sentEmailPattern.matcher(text);
			
			while (sentEmailMatch.find()) {
				// pull out the email address from the line
				Matcher matcher = emailPattern.matcher(sentEmailMatch.group());
		        matcher.find();
		        String email = matcher.group();
		        sentEmail.add("<" + email + ">");
		        
				sentDomainParsed = false;
			}
		} catch (Exception e) {
			throw new Exception("Failed to match the sender email address regex: " + e.getMessage());
		}
	}

	/**Parse out the received email address based on the defined regex for the mbox format.
	 * 
	 * @param text The text to parse and see if the received email address is in it
	 * 
	 * @throws Exception
	 */
	private void parseReceivedEmail(String text) throws Exception {
		try {
			// received email pattern match
			Matcher receivedEmailMatch = receivedEmailPattern.matcher(text);
			
			while (receivedEmailMatch.find()) {
				// pull out the email address from the line
				Matcher matcher = emailPattern.matcher(receivedEmailMatch.group());
		        matcher.find();
		        String email = matcher.group();
		        receivedEmail.add("<" + email + ">");
			}
		} catch (Exception e) {
			throw new Exception("Failed to match the received email address regex: " + e.getMessage());
		}
	}

	/**Parse out the sent domain based on the defined regex for the mbox format.
	 * 
	 * @param text The text to parse and see if the sent domain is in it
	 * 
	 * @throws Exception
	 */
	private void parseSentDomain(String text) throws Exception {
		// account for multiple lines matching the pattern per email
		if (sentDomainParsed)
			return;
		
		try {
			// sent email pattern match
			Matcher sentDomainMatch = sentDomainPattern.matcher(text);
			
			while (sentDomainMatch.find()) {
				// pull out the domain from the line
				String domain = sentDomainMatch.group(2);
		        sentDomain.add("<" + domain + ">");
		        
				sentDomainParsed = true;
			}
		} catch (Exception e) {
			throw new Exception("Failed to match the sender domain regex: " + e.getMessage());
		}
	}
	
	/**Parse out the received domain based on the defined regex for the mbox format.
	 * 
	 * @param text The text to parse and see if the received domain is in it
	 * 
	 * @throws Exception
	 */
	private void parseReceivedDomain(String text) throws Exception {
		try {
			// sent email pattern match
			Matcher receivedDomainMatch = receivedDomainPattern.matcher(text);
			
			while (receivedDomainMatch.find()) {
				// pull out the domain from the line
				String domain = receivedDomainMatch.group(2);
		        receivedDomain.add("<" + domain + ">");
			}
		} catch (Exception e) {
			throw new Exception("Faild to match the receiver domain regex: " + e.getMessage());
		}

	}
	
	/**Parse out the sent subject based on the defined regex for the mbox format.
	 * 
	 * @param text The text to parse and see if the sent subject is in it
	 * 
	 * @throws Exception
	 */
	private void parseSubject(String text) throws Exception {
		try {
			// sent subject line pattern match
			Matcher sentSubjectMatch = sentSubjectPattern.matcher(text);
			
			while (sentSubjectMatch.find()) {
				// pull out the subject of the email from the line
				String s = sentSubjectMatch.group(1);
				sentSubject.add("<" + s + ">: IP <" + lastParsedSentIp + ">");
				receivedSubject.add("<" + s + ">: IP <" + lastParsedReceivedIp + ">");
			}
		} catch (Exception e) {
			throw new Exception("Failed to match the sent subject line regex: " + e.getMessage());
		}
	}

	@Override
	public List<String> getSentIP() {
		return sentIP;
	}

	@Override
	public List<String> getReceivedIP() {
		return receivedIP;
	}

	@Override
	public List<String> getSentEmail() {
		return sentEmail;
	}

	@Override
	public List<String> getReceivedEmail() {
		return receivedEmail;
	}

	@Override
	public List<String> getSentDomain() {
		return sentDomain;
	}

	@Override
	public List<String> getReceivedDomain() {
		return receivedDomain;
	}

	@Override
	public List<String> getSentSubject() {
		return sentSubject;
	}
	
	@Override
	public List<String> getReceivedSubject() {
		return receivedSubject;
	}

	@Override
	public void printStats() throws Exception {
//		if (sentIP.isEmpty() || receivedIP.isEmpty() || sentEmail.isEmpty() || receivedEmail.isEmpty() ||
//			sentDomain.isEmpty() || receivedDomain.isEmpty() || sentSubject.isEmpty() || receivedSubject.isEmpty()) {
//			
//			throw new Exception("MboxFile.printStats(): File either has no stats in it, or has not been parsed");
//		}
		
		System.out.println("Sender IP addresses: " + sentIP.toString());
		System.out.println("Receiver IP addresses: " + receivedIP.toString());
		System.out.println("Sender email addresses: " + sentEmail.toString());
		System.out.println("Receiver email addresses: " + receivedEmail.toString());
		System.out.println("Sender domain: " + sentDomain.toString());
		System.out.println("Receiver domain: " + receivedDomain.toString());
		System.out.println("Sender subject line: " + sentSubject.toString());
		System.out.println("Receiver subject line: " + receivedSubject.toString());
	}
}
